package com.colin.userservice.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.Hutool;
import cn.hutool.captcha.generator.RandomGenerator;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.crypto.digest.MD5;
import com.aliyun.oss.OSS;
import com.aliyun.oss.model.PutObjectRequest;
import com.colin.bean.User;
import com.colin.bean.UserFollow;
import com.colin.response.R;
import com.colin.response.RCM;
import com.colin.userservice.config.AliyunSMSConfig;
import com.colin.userservice.exception.RegisterException;
import com.colin.userservice.exception.sub.PasswordErrorException;
import com.colin.userservice.exception.sub.UserNotExistException;
import com.colin.userservice.mapper.UserMapper;
import com.colin.userservice.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Random;
import java.util.UUID;

@Service
@Slf4j
public class UserServiceImpl implements UserService {


    private static final String CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    private static final int USERNAME_LENGTH = 8;
    private static final String USERNAME_PREFIX = "user_";

    /**
     * 通过雪花算法可以获取分布式ID，全局唯一且按时间递增
     * 如果后续数据库需要分表，通过数据库自增id就会受限，不便分表
     * 如果使用雪花算法可以生成全局唯一id，不会影响分表
     */
    @Autowired
    private Snowflake snowflake;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    AliyunSMSConfig messageConfig;

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    private OSS ossClient;

    @Value("${aliyun.oss.bucket-name}")
    private String bucketName;

    @Value("${aliyun.oss.endpoint}")
    private String endpoint;



    private String generateRandomString(Integer length) {
        Random random = new Random();
        StringBuilder sb = new StringBuilder(length);
        for (int i = 0; i < length; i++) {
            sb.append(CHARACTERS.charAt(random.nextInt(CHARACTERS.length())));
        }
        return sb.toString();
    }

    /**
     * 先验证用户是否能登录
     * 在能登录的情况下，通过长短Token保存登录态
     *
     * @param user
     * @return
     */
    @Override
    public R<User> doLogin(User user) {
        String phoneNum = user.getPhoneNum();
        //获得用户的盐
        String salt = userMapper.selectSaltByUserPhoneNum(phoneNum);
//        String salt = userMapper.selectSaltByUsername(user.getUsername());
        String username = user.getUsername();
        log.info("用户电话：{}对应的salt：{}", phoneNum, salt);
        if (salt == null) {
            log.error("用户电话为：{}的用户不存在", user.getPhoneNum());
            throw new UserNotExistException(RCM.USER_NOT_EXISTS.getMessage());
        }
        //二次加盐加密
        MD5 md5 = MD5.create();
        String finalPassword = md5.digestHex16(user.getPassword() + ":" + salt, StandardCharsets.UTF_8);
        log.info("最终密码为{}", finalPassword);
        //用二次加盐加密的username和password进行查询
        user = userMapper.selectUserByPhoneNumAndPassword(phoneNum, finalPassword);
        if (user == null) {
            log.error("用户电话为：{}的用户密码错误", phoneNum);
            throw new PasswordErrorException(RCM.USER_PASSWORD_ERROR.getMessage());
        }
        //处理敏感信息，避免将用户数据传回前端被捕获
        user.setPassword(null);
        user.setSalt(null);
        user.setPhoneNum(null);
        user.setLastLogin(LocalDateTime.now());
        //服务端保存登录态
        //StpUtil.login()传入一个和当前登录用户相关的唯一标识
        StpUtil.login(user.getId());
        return R.ok(user);
    }

    /**
     * 先通过雪花算法生成全局id
     * 生成随机盐
     * 一层加密密码为password:salt
     * 二层加密嵌套md5
     * 向数据库传入最后二次加密的密码
     * 即使攻击数据库单靠salt也无法获取真实密码
     *
     * @param user
     * @param smsCode
     * @return
     */
    @Override
    public R<Boolean> doRegister(User user, String smsCode) {
        //判断该电话号是否被注册过
        String phoneNum = user.getPhoneNum();
        if (userMapper.isExistUser(phoneNum) != null) {
            log.error("该用户：{} 已经注册 抛出异常为RegisterException", phoneNum);
            throw new RegisterException(RCM.USER_EXISTS.getMessage());
        }
        //验证验证码是否正确
        System.out.println(messageConfig.getVerificationCodeFromRedis(phoneNum));
        if (!messageConfig.getVerificationCodeFromRedis(phoneNum).equals(smsCode)) {
            log.error("验证码错误");
            throw new RegisterException(RCM.USER_SMS_ERROR.getMessage());
        }
        //通过雪花算法生成全局id
        long id = snowflake.nextId();

        log.info("雪花算法生成id：{}", id);
        //生成6位随机盐
        RandomGenerator randomGenerator = new RandomGenerator(6);
        String salt = Base64.encode(randomGenerator.generate());
        log.info("生成随机盐salt：{}", salt);
        //二次加盐加密 password:salt-> md5(password:salt)
        MD5 md5 = MD5.create();
        String finalPassword = md5.digestHex16(user.getPassword() + ":" + salt, StandardCharsets.UTF_8);
        log.info("最终密码：{}", finalPassword);
        //生成随机用户名
        String username;
        do {
            username = USERNAME_PREFIX + generateRandomString(USERNAME_LENGTH);
        } while (userMapper.isExistUser(username) != null);

        //user封装
        user.setId(id);
        user.setUsername(username);
        user.setPassword(finalPassword);
        user.setSalt(salt);
        user.setAvatarUrl(null);
        user.setSignupTime(LocalDateTime.now());
        user.setLevel(1);
        user.setDescription("这个人很懒，还没有个性签名");
        user.setExp(0);
        user.setSex("男");
        user.setBirthday(null);
        user.setAddress(null);
        System.out.println(user);
        Boolean insertResult = userMapper.insertUser(user);
        if (!insertResult) {
            log.error("电话号：{}注册失败 抛出异常为RegisterException", phoneNum);
            throw new RegisterException(RCM.USER_REGISTER_ERROR.getMessage());
        }
        return R.ok(true);
    }

    /**
     * 发送短信验证码到用户
     * @param phoneNum
     * @return
     */
    @Override
    public R<String> doCode(String phoneNum) {
        return messageConfig.sendVerificationCode(phoneNum);
    }

    /**
     * 获取用户关注列表
     * @return
     */
    @Override
    public R<List<User>> getFollowList() {
        String loginId = (String) StpUtil.getLoginId();
        return R.ok(userMapper.getFollowList(loginId));
    }

    /**
     * 做出关注行为
     * @param userId
     * @return
     */
    @Override
    public R<Boolean> doFollow(String userId) {
        String loginId = (String) StpUtil.getLoginId();
        //检测是否该数据已经在关注表中
        if (userMapper.checkFollow(loginId, userId)!=null) {
            return R.ok(userMapper.updateFollow(loginId, userId, LocalDateTime.now()));
        }
        return R.ok(userMapper.addFollow(loginId, userId ,LocalDateTime.now()));
    }

    /**
     * 取消关注
     * @param userId
     * @return
     */
    @Override
    public R<Boolean> doUnFollow(String userId) {
        String loginId = (String) StpUtil.getLoginId();
        return R.ok(userMapper.deleteFollow(loginId, userId));
    }

    /**
     * 模糊搜索
     * @param description
     * @return
     */
    @Override
    public R<List<User>> getSearch(String description) {
        return R.ok(userMapper.searchUser(description)) ;
    }

    /**
     * 更新用户信息
     * @param user
     * @return
     */
    @Override
    public R<Boolean> doUpdate(User user) {
        String loginId = (String) StpUtil.getLoginId();
        user.setId(Long.parseLong(loginId));
        return R.ok(userMapper.updateUser(user));
    }

    /**
     * 上传用户头像
     * @param avatar
     * @return
     */
    @Override
    public R<Boolean> uploadAvatar(MultipartFile avatar) {
        String loginId = (String) StpUtil.getLoginId();
        // 1. 生成唯一的对象键（Object Key），即存储在OSS上的文件路径和名称
        String objectKey = generateObjectKey(avatar.getOriginalFilename(), loginId);

        // 2. 执行上传
        try {
            // 直接使用Spring提供的MultipartFile的InputStream
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectKey, avatar.getInputStream());
            ossClient.putObject(putObjectRequest);

            // 3. 构建并返回文件的访问URL
            String cleanEndpoint = endpoint.replaceAll("^https?://", "");
            // 构建标准OSS外网访问域名格式
            String url = "https://" + bucketName + "." + cleanEndpoint + "/" + objectKey;
            log.info("上传上去的头像url为：{}", url);
//            return R.ok(true);
            return R.ok(userMapper.updateAvatorUrl(loginId,url));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 生成文件名
     * @param originalFilename
     * @param userId
     * @return
     */
    private String generateObjectKey(String originalFilename, String userId) {
        // 获取文件后缀名，如 .jpg
        String suffix = "";
        if (originalFilename != null && originalFilename.contains(".")) {
            suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
        }
        // 生成UUID和当前时间戳
        String uuid = UUID.randomUUID().toString().replace("-", "");
        String datePath = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));

        // 组合成完整的对象键
        return String.format("avatars/%s/%s/%s_%d%s", userId, datePath, uuid, System.currentTimeMillis(), suffix);
    }

    /**
     * 获取用户信息
     * @return
     */
    @Override
    public R<User> getUserDetail() {
        String loginId = (String) StpUtil.getLoginId();
        User user = userMapper.getUserById(loginId);
        user.setPassword(null);
        user.setId(null);
        user.setSalt(null);
        return R.ok(user);
    }

    @Override
    public R<UserFollow> getFollowStatus(String userId) {
        String loginId = (String) StpUtil.getLoginId();
        return R.ok(userMapper.getFollow(loginId, userId));
    }
}
